<script setup lang="ts">
import * as THREE from "three";
import { ref, onMounted, type Ref, h } from "vue";
import { OrbitControls } from "three/addons/controls/OrbitControls.js";
import gsap from "gsap";
import * as dat from "dat.gui";

// 初始化场景
const scene = new THREE.Scene();

// 创建相机
const camera = new THREE.PerspectiveCamera(
  75,
  window.innerWidth / window.innerHeight,
  0.1,
  1000
);

// 设置相机位置
// camera.position.z = 30;

// 相机放入到场景中
camera.position.set(0, 0, 0.01);

const useBox = () => {
  const materials: THREE.MeshBasicMaterial[] = [];

  const texture_right = new THREE.TextureLoader().load(
    "./images/livingRoom/living_r.jpg"
  );
  materials.push(new THREE.MeshBasicMaterial({ map: texture_right }));

  const texture_left = new THREE.TextureLoader().load(
    "./images/livingRoom/living_l.jpg"
  );
  materials.push(new THREE.MeshBasicMaterial({ map: texture_left }));

  const texture_up = new THREE.TextureLoader().load(
    "./images/livingRoom/living_u.jpg"
  );
  materials.push(new THREE.MeshBasicMaterial({ map: texture_up }));

  const texture_down = new THREE.TextureLoader().load(
    "./images/livingRoom/living_d.jpg"
  );
  materials.push(new THREE.MeshBasicMaterial({ map: texture_down }));

  const texture_front = new THREE.TextureLoader().load(
    "./images/livingRoom/living_f.jpg"
  );
  materials.push(new THREE.MeshBasicMaterial({ map: texture_front }));

  const texture_back = new THREE.TextureLoader().load(
    "./images/livingRoom/living_b.jpg"
  );
  materials.push(new THREE.MeshBasicMaterial({ map: texture_back }));

  // 创建几何体
  const geometry = new THREE.BoxGeometry(10, 10, 10);
  // 创建材质
  // const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
  // 创建网格
  const box = new THREE.Mesh(geometry, materials);
  box.geometry.scale(1, 1, -1);
  // 将网格添加到场景中
  scene.add(box);
};

const useSphere = () => {
  const sphereGeometry = new THREE.SphereGeometry(1, 50, 50);
  sphereGeometry.scale(1, 1, -1);
  const texture = new THREE.TextureLoader().load("./images/scene.jpeg");
  const sphereMaterial = new THREE.MeshBasicMaterial({ map: texture });

  const sphere = new THREE.Mesh(sphereGeometry, sphereMaterial);
  scene.add(sphere);
};

// useBox();
// useSphere();

window.addEventListener("resize", () => {
  // 更新相机的宽高比
  camera.aspect = window.innerWidth / window.innerHeight;
  // 更新相机的投影矩阵
  camera.updateProjectionMatrix();
  // 更新渲染器的大小
  renderer.setSize(window.innerWidth, window.innerHeight);
});

const materials: THREE.MeshBasicMaterial[] = [];

const texture_right = new THREE.TextureLoader().load(
  "./images/livingRoom/living_r.jpg"
);
materials.push(new THREE.MeshBasicMaterial({ map: texture_right }));

const texture_left = new THREE.TextureLoader().load(
  "./images/livingRoom/living_l.jpg"
);
materials.push(new THREE.MeshBasicMaterial({ map: texture_left }));

const texture_up = new THREE.TextureLoader().load(
  "./images/livingRoom/living_u.jpg"
);
materials.push(new THREE.MeshBasicMaterial({ map: texture_up }));

const texture_down = new THREE.TextureLoader().load(
  "./images/livingRoom/living_d.jpg"
);
materials.push(new THREE.MeshBasicMaterial({ map: texture_down }));

const texture_front = new THREE.TextureLoader().load(
  "./images/livingRoom/living_f.jpg"
);
materials.push(new THREE.MeshBasicMaterial({ map: texture_front }));

const texture_back = new THREE.TextureLoader().load(
  "./images/livingRoom/living_b.jpg"
);
materials.push(new THREE.MeshBasicMaterial({ map: texture_back }));

// 客厅立方体
// 创建几何体
const geometry = new THREE.BoxGeometry(10, 10, 10);
// const v3 = new THREE.Vector3(0, 0, 0);
// const e3 = new THREE.Euler(0, 10, 0);
// 创建材质
// const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
// 创建网格
const box = new THREE.Mesh(geometry, materials);
box.geometry.scale(1, 1, -1);

// box.position.x = 10;
// box.position.y = 10;
// box.position.set(10, 10, 0);
// box.position.copy(v3);
// box.rotation.copy(e3);
// 将网格添加到场景中
scene.add(box);

// 阳台立方体
const roomPrefix = "balcony";
const arr = [
  `${roomPrefix}_r`,
  `${roomPrefix}_l`,
  `${roomPrefix}_u`,
  `${roomPrefix}_d`,
  `${roomPrefix}_f`,
  `${roomPrefix}_b`,
];
const balconyMaterials: THREE.MeshBasicMaterial[] = [];
arr.forEach((item) => {
  const texture = new THREE.TextureLoader().load(
    `./images/balcony/${item}.jpg`
  );
  balconyMaterials.push(new THREE.MeshBasicMaterial({ map: texture }));
});

const balconyGeometry = new THREE.BoxGeometry(10, 10, 10);
const balconyV3 = new THREE.Vector3(0, 0, -10);
const balconyE3 = new THREE.Euler(0, 0, 0);
const balconyBox = new THREE.Mesh(balconyGeometry, balconyMaterials);
balconyBox.geometry.scale(1, 1, -1);
balconyBox.position.copy(balconyV3);
balconyBox.rotation.copy(balconyE3);
scene.add(balconyBox);

// gsap.to(box.position, { x: 5, duration: 2 });
// gsap.to(box.position, { y: 5, duration: 1 });
// gsap.to(box.rotation, { z: 5, duration: 2 });

// 定义阳台导航的canvas
const canvas = document.createElement("canvas");
canvas.width = 1024;
canvas.height = 1024;
const context = canvas.getContext("2d")!;
context.fillStyle = "rgba(100,100,100,.7)";
context.fillRect(0, 256, canvas.width, canvas.height / 2);
context.textAlign = "center";
context.textBaseline = "middle";
context.font = "bold 200px Arial";
context.fillStyle = "white";
context.fillText("阳台", canvas.width / 2, canvas.height / 2);

const balconySpriteTexture = new THREE.CanvasTexture(canvas);

// 创建阳台导航
const balconySpriteMaterial = new THREE.SpriteMaterial({
  map: balconySpriteTexture,
  transparent: true,
});

const balconySprite = new THREE.Sprite(balconySpriteMaterial);
const balconySpritePosition = new THREE.Vector3(0, 0, -4);
balconySprite.position.copy(balconySpritePosition);
scene.add(balconySprite);

const tooltipBox = ref<HTMLElement | null>(null);
const tooltipPosition = ref({
  left: "-100%",
  top: "-100%",
});
const tooltipContent: Ref<Record<string, any>> = ref({
  name: "",
  description: "",
});

// 创建信息点
const informationTexture = new THREE.TextureLoader().load("./images/dot.png");
const informationSpriteMaterial = new THREE.SpriteMaterial({
  map: informationTexture,
  transparent: true,
});
const informationSprite = new THREE.Sprite(informationSpriteMaterial);
const informationSpritePosition = new THREE.Vector3(1.5, -0.1, -3);
informationSprite.position.copy(informationSpritePosition);
informationSprite.scale.set(0.2, 0.2, 0.2);
informationSprite.userData = {
  type: "information",
  name: "信息点",
  description: "这是一个信息点",
};
scene.add(informationSprite);

const poiObjects: THREE.Sprite[] = [];
poiObjects.push(balconySprite);
poiObjects.push(informationSprite);

const raycaster = new THREE.Raycaster();
const pointer = new THREE.Vector2();

function tooltipShow(event: MouseEvent) {
  event.preventDefault();
  // 将鼠标位置归一化为设备坐标。x 和 y 方向的取值范围是 (-1 to +1)
  pointer.x = (event.clientX / window.innerWidth) * 2 - 1;
  pointer.y = -(event.clientY / window.innerHeight) * 2 + 1;

  // 通过摄像机和鼠标位置更新射线
  raycaster.setFromCamera(pointer, camera);
  // 计算物体和射线的交点
  const intersects = raycaster.intersectObjects(poiObjects);
  if (intersects.length > 0) {
    if (intersects[0].object.userData.type === "information") {
      // 最重要的是我们要设置弹出层的left和top属性
      // 所以我们需要获取鼠标位置
      // 这就需要我们将三维空间中的坐标转换为二维屏幕坐标
      // Vector3对象的project方法可以将三维坐标转换为NDC坐标
      // NDC坐标是一个范围在[-1, 1]之间的坐标系
      // 所以我们需要将NDC坐标转换为屏幕坐标
      // 比如有下面的坐标
      // const vector = new THREE.Vector3(10, 20, 30);
      // vector.project(camera);
      // 通过下面的公式可以将NDC坐标转换为屏幕坐标
      // x = (vector.x + 1) * (width(默认全屏canvas) / 2)
      // y = (-vector.y + 1) * (height(默认全屏canvas) / 2)
      const element = event.target as HTMLElement;
      const elementWidth = element.clientWidth / 2;
      const elementHeight = element.clientHeight / 2;
      const worldVector = new THREE.Vector3(
        intersects[0].object.position.x,
        intersects[0].object.position.y,
        intersects[0].object.position.z
      );
      const position = worldVector.project(camera);

      let left = Math.round(
        elementWidth * position.x +
        elementWidth -
        tooltipBox.value!.clientWidth / 2
      );

      let top = Math.round(
        -elementHeight * position.y +
        elementHeight -
        tooltipBox.value!.clientHeight / 2
      );

      tooltipPosition.value = {
        left: `${left}px`,
        top: `${top}px`,
      };

      tooltipContent.value = intersects[0].object.userData;
    }
  }
  else { 
    handleTooltipHide(event);
  }
}

function handleTooltipHide(event: MouseEvent) {
  event.preventDefault();
  tooltipPosition.value = {
    left: "-100%",
    top: "-100%",
  };
  tooltipContent.value = {
    name: "",
    description: "",
  };
}

// 添加点击事件
window.addEventListener("click", (event: MouseEvent) => {
  event.preventDefault();
  // 将鼠标位置归一化为设备坐标。x 和 y 方向的取值范围是 (-1 to +1)
  pointer.x = (event.clientX / window.innerWidth) * 2 - 1;
  pointer.y = -(event.clientY / window.innerHeight) * 2 + 1;

  // 通过摄像机和鼠标位置更新射线
  raycaster.setFromCamera(pointer, camera);
  // 计算物体和射线的交点
  const intersects = raycaster.intersectObjects(poiObjects);
  if (intersects.length > 0) {
    gsap.to(camera.position, {
      duration: 1,
      x: 0,
      y: 0,
      z: -10,
    });
  }
});

// 图形化控制界面
const gui = new dat.GUI();
// gui.add(box.position, 'x').name('移动x轴').min(-5).max(5).step(0.1);
// gui.add(box.position, 'y').name('移动y轴').min(-5).max(5).step(0.1);
// gui.add(box.position, 'z').name('移动z轴').min(-5).max(5).step(0.1);
// gui.addColor({ color: 0x00ff00 }, 'color').name('修改颜色').onChange((e) => {
//   box.material.color = new THREE.Color(e);
// })

const boxFolder = gui.addFolder("立方体属性");
boxFolder.add(box.position, "x").name("移动x轴").min(-5).max(5).step(0.1);
boxFolder.add(box.position, "y").name("移动y轴").min(-5).max(5).step(0.1);
boxFolder.add(box.position, "z").name("移动z轴").min(-5).max(5).step(0.1);
boxFolder.add(box.rotation, "x").name("旋转x轴").min(-5).max(5).step(0.1);
boxFolder.add(box.rotation, "y").name("旋转y轴").min(-5).max(5).step(0.1);
boxFolder.add(box.rotation, "z").name("旋转z轴").min(-5).max(5).step(0.1);
boxFolder
  .addColor({ color: 0x00ff00 }, "color")
  .name("修改颜色")
  .onChange((e) => {
    // box.material.color = new THREE.Color(e);
  });

const params = {
  visible: true,
  moveAnimation: () => {
    gsap.to(box.position, { x: 5, duration: 2 });
  },
  cameraAnimation: () => {
    gsap.to(camera.position, {
      duration: 1,
      x: 0,
      y: 0,
      z: -10,
    });
  },
};

boxFolder
  .add(params, "visible")
  .name("是否可见")
  .onChange((e) => {
    box.visible = e;
  });
boxFolder.add(params, "moveAnimation").name("移动动画");
boxFolder.open();

const cameraFolder = gui.addFolder("相机属性");
cameraFolder
  .add(camera.position, "x")
  .name("相机x轴")
  .min(-30)
  .max(30)
  .step(0.1);
cameraFolder
  .add(camera.position, "y")
  .name("相机y轴")
  .min(-30)
  .max(30)
  .step(0.1);
cameraFolder
  .add(camera.position, "z")
  .name("相机z轴")
  .min(-30)
  .max(30)
  .step(0.1);
cameraFolder
  .add(camera.rotation, "x")
  .name("相机旋转x轴")
  .min(-5)
  .max(5)
  .step(0.1);
cameraFolder
  .add(camera.rotation, "y")
  .name("相机旋转y轴")
  .min(-5)
  .max(5)
  .step(0.1);
cameraFolder
  .add(camera.rotation, "z")
  .name("相机旋转z轴")
  .min(-5)
  .max(5)
  .step(0.1);

cameraFolder.add(params, "cameraAnimation").name("相机移动位置");

cameraFolder.open();

// 轨道控制器对象
let controls: OrbitControls;
// 坐标辅助器
const axesHelper = new THREE.AxesHelper(20);
scene.add(axesHelper);

// 创建渲染器
const renderer = new THREE.WebGLRenderer();
// 设置渲染器大小
renderer.setSize(window.innerWidth, window.innerHeight);

const container = ref<HTMLElement | null>(null);

const render = () => {
  // box.position.x += 0.01;
  // box.rotation.x += 0.01;
  // if(box.position.x > 5) {
  //   box.position.x = 0;
  //   box.rotation.x = 0;
  // }
  renderer.render(scene, camera);
  requestAnimationFrame(render);
};

onMounted(() => {
  if (container.value) {
    // 添加轨道控制器
    // controls = new OrbitControls(camera, renderer.domElement);
    // controls.enableDamping = true;
    // controls.update();

    container.value.appendChild(renderer.domElement);
    render();

    let isMouseDown = false;
    container.value.addEventListener(
      "mousedown",
      () => {
        isMouseDown = true;
      },
      false
    );
    container.value.addEventListener(
      "mouseup",
      () => {
        isMouseDown = false;
      },
      false
    );
    container.value.addEventListener(
      "mouseout",
      () => {
        isMouseDown = false;
      },
      false
    );

    container.value.addEventListener("mousemove", (event) => {
      if (isMouseDown) {
        camera.rotation.x += event.movementY * 0.01;
        camera.rotation.y += event.movementX * 0.01;
        camera.rotation.order = "YXZ";
      }
    });

    renderer.domElement.addEventListener("mousemove", tooltipShow);
    tooltipBox.value!.addEventListener("mouseleave", handleTooltipHide);
  }
});
</script>

<template>
  <div ref="container" class="container"></div>
  <!-- 信息点弹出层 -->
  <div class="tooltip-box" :style="tooltipPosition" ref="tooltipBox">
    <div class="wrapper">
      <div class="name">标题: {{ tooltipContent.name }}</div>
      <div class="description">说明: {{ tooltipContent.description }}</div>
    </div>
  </div>
</template>

<style lang="scss">
* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}
.container {
  width: 100vw;
  height: 100vh;
}
.tooltip-box {
  position: absolute;
  padding: 0px 0px 40px 0px;
  line-height: 30px;
  border-radius: 4px;
  color: #fff;
  z-index: 100;
  cursor: pointer;
  .wrapper {
    position: relative;
    width: 240px;
    max-height: 200px;
    padding: 10px;
    background-color: rgba(0, 0, 0, 0.6);
    .name {
      width: 100%;
      padding: 6px 0;
    }
    .description {
      width: 100%;
      max-height: 100px;
      font-size: 14px;
      overflow-y: auto;
    }
  }
}
</style>
